package com.wrbug.mmkw.compile

import com.squareup.javapoet.ArrayTypeName
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.TypeName
import com.sun.tools.javac.code.Attribute
import com.sun.tools.javac.code.Symbol
import com.sun.tools.javac.code.Type
import com.sun.tools.javac.util.List
import com.wrbug.mmkw.annotation.KwGet
import com.wrbug.mmkw.annotation.KwRemove
import com.wrbug.mmkw.annotation.KwSet

object MethodParser {
    private const val PATTERN_SET = "set[A-Z].*"
    private const val PATTERN_GET = "get[A-Z].*"
    private const val PATTERN_REMOVE = "remove[A-Z].*"
    fun parse(methodSymbol: Symbol.MethodSymbol): MethodInfo {
        val methodName =
            if (methodSymbol.name === methodSymbol.name.table.names.init) methodSymbol.owner.name.toString() else methodSymbol.name.toString()
        val info = MethodInfo(methodName)
        val methodType = methodSymbol.type as Type.MethodType
        val attributes = methodSymbol.metadata?.declarationAttributes
        getMmkvType(methodSymbol, methodName, attributes, info)
        info.returnType = ClassName.get(methodType.restype)
        if (methodType.argtypes?.size == 1) {
            info.parameter = ClassName.get(methodType.argtypes[0])
        }
        getMmkvReturnType(info)
        return info
    }

    private fun getMmkvReturnType(info: MethodInfo) {
        info.mmkvReturnType = when {
            TypeName.BOOLEAN == info.returnType -> {
                MmkvReturnType.BOOL
            }
            TypeName.INT == info.returnType -> {
                MmkvReturnType.INT
            }
            TypeName.LONG == info.returnType -> {
                MmkvReturnType.LONG
            }
            TypeName.FLOAT == info.returnType -> {
                MmkvReturnType.FLOAT
            }
            TypeName.DOUBLE == info.returnType -> {
                MmkvReturnType.DOUBLE
            }
            ClassName.get(String::class.java) == info.returnType -> {
                MmkvReturnType.STRING
            }
            ArrayTypeName.get(Byte::class.java) == info.returnType -> {
                MmkvReturnType.BYTES
            }
            else -> {
                info.needConvert = true
                MmkvReturnType.STRING
            }
        }
    }


    private fun getMmkvType(
        methodSymbol: Symbol.MethodSymbol,
        methodName: String,
        attributes: List<Attribute.Compound>?,
        info: MethodInfo
    ) {
        attributes?.forEach {
            val annotationName = it.type.toString()
            if (annotationName == KwGet::class.java.canonicalName) {
                info.mmkvType = MmkvType.GET
                val annotation = methodSymbol.getAnnotation(KwGet::class.java)
                if (annotation.key.isNotEmpty()) {
                    info.key = annotation.key
                } else {
                    info.key = parseKeyFromMethodName(methodName)
                }
                return
            }
            if (annotationName == KwSet::class.java.canonicalName) {
                info.mmkvType = MmkvType.SET
                val annotation = methodSymbol.getAnnotation(KwSet::class.java)
                if (annotation.key.isNotEmpty()) {
                    info.key = annotation.key
                } else {
                    info.key = parseKeyFromMethodName(methodName)
                }
                return
            }
            if (annotationName == KwRemove::class.java.canonicalName) {
                info.mmkvType = MmkvType.REMOVE
                val annotation = methodSymbol.getAnnotation(KwRemove::class.java)
                if (annotation.key.isNotEmpty()) {
                    info.key = annotation.key
                    return
                } else {
                    info.key = parseKeyFromMethodName(methodName)
                }
                return
            }
        }
        info.mmkvType = parseMmkvTypeFromMethodName(methodName)
        info.key = parseKeyFromMethodName(methodName)
    }

    private fun parseMmkvTypeFromMethodName(methodName: String): MmkvType {
        if (methodName.matches(Regex(PATTERN_SET))) {
            return MmkvType.SET
        }
        if (methodName.matches(Regex(PATTERN_GET))) {
            return MmkvType.GET
        }
        if (methodName.matches(Regex(PATTERN_REMOVE))) {
            return MmkvType.REMOVE
        }
        return MmkvType.UN_KNOW
    }

    private fun parseKeyFromMethodName(methodName: String): String {
        val mmkvType = parseMmkvTypeFromMethodName(methodName)
        if (mmkvType == MmkvType.SET) {
            return "${methodName.toCharArray()[3].toLowerCase()}${methodName.substring(4)}"
        }
        if (mmkvType == MmkvType.GET) {
            return "${methodName.toCharArray()[3].toLowerCase()}${methodName.substring(4)}"
        }
        if (mmkvType == MmkvType.REMOVE) {
            return "${methodName.toCharArray()[6].toLowerCase()}${methodName.substring(7)}"
        }
        return methodName
    }
}